package com.nec.recommend.handler;

import cn.hutool.core.util.StrUtil;
import com.nec.recommend.common.Result;
import com.nec.recommend.exception.ServiceException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@RestControllerAdvice
public class GlobalExceptionHandler {
    private final Log log = LogFactory.getLog(GlobalExceptionHandler.class);

    /**
     * 全局异常捕获
     *
     * @param e 全局异常
     * @return 响应结果
     */
    @ExceptionHandler(Exception.class)
    public <T> Result<?> globalException(Exception e) {
        log.error(e);
        return Result.failed(e.getMessage());
    }

    /**
     * 不支持的请求
     *
     * @param e 不支持请求异常
     * @return 数据返回
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public <T> Result<?> notSupportHandler(HttpRequestMethodNotSupportedException e) {
        return Result.failed("不支持的请求：" + e.getMethod());
    }

    /**
     * 内容类型的请求
     *
     * @param e 内容类型的请求异常
     * @return 数据返回
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public <T> Result<?> mediaTypeNotSupportHandler(HttpMediaTypeNotSupportedException e) {
        return Result.failed("不支持的媒体类型");
    }

    /**
     * 不能读取异常的请求
     *
     * @param e 不支持请求异常
     * @return 数据返回
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public <T> Result<?> notReadableHandler(HttpMessageNotReadableException e) {
        return Result.failed("无法读取错误");
    }

    //声明要捕获的异常
    @ExceptionHandler(ServiceException.class)
    public <T> Result<?> serviceExceptionHandler(ServiceException e) {
        log.error(e);
        return Result.failed(e.getMessage());
    }

    //声明要捕获的异常
    @ExceptionHandler(BindException.class)
    public <T> Result<?> bindExceptionHandler(BindException e) {
        return Result.failed("参数绑定异常：" + bindResolve(e.getBindingResult()));
    }

    /**
     * 处理@Valid校验异常
     *
     * @param e 校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public <T> Result<?> argumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        return Result.failed(bindResolve(e.getBindingResult()));
    }

    /**
     * 处理校验单参数异常
     *
     * @param ex 校验异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public <T> Result<?> constraintViolationExceptionHandler(ConstraintViolationException ex) {
        //获取全部校验异常对象并迭代获取错误信息
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
        List<String> msgList = new ArrayList<>(constraintViolations.size());
        while (iterator.hasNext()) {
            ConstraintViolation<?> cvl = iterator.next();
            msgList.add(cvl.getMessageTemplate());
        }
        //组装错误信息并返回
        return Result.failed(resolveFieldsMsg(msgList));
    }


    /**
     * 绑定解析
     *
     * @param bindingResult 绑定结果
     */
    private String bindResolve(BindingResult bindingResult) {
        //获取全部异常信息
        List<String> msgList = bindingResult.getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.toList());
        return resolveFieldsMsg(msgList);
    }


    /**
     * 解析字段信息
     *
     * @param msgList 错误信息集合
     * @return 拼接后的信息
     */
    private String resolveFieldsMsg(List<String> msgList) {
        //创建Builder
        StringBuilder builder = new StringBuilder();
        msgList.forEach(v -> builder.append(StrUtil.COMMA).append("[").append(v).append("]"));
        return builder.deleteCharAt(0).toString();
    }
}